package com.gt.wxpay.cashier.payChannel.callback.handler;

import com.gt.wxpay.cashier.common.runable.NotifyThreadRunnable;
import com.gt.wxpay.cashier.domain.dto.ChannelRCRate;
import com.gt.wxpay.cashier.domain.model.CashierPayment;
import com.gt.wxpay.cashier.domain.model.PayTypeEnum;
import com.gt.wxpay.cashier.notifyRecord.application.NotifyRecordService;
import com.gt.wxpay.cashier.payChannel.callback.vo.WxNotifyVo;
import com.gt.wxpay.cashier.payChannel.handler.OperationWxHandler;
import com.gt.wxpay.cashier.payment.application.PaymentService;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;

import static com.wechat.pay.java.core.http.Constant.*;

/**
 * packageName com.gt.wxpay.cashier.payChannel.callback.handler
 *
 * @author GuoTong
 * @version JDK 8
 * @className WxPayCallBackNotifyService
 * @date 2024/3/22
 * @description 微信支付通知回调业务处理
 */
@Component
@SuppressWarnings("all")
public class WxPayCallBackNotifyService {

    private static Logger log = LoggerFactory.getLogger(WxPayCallBackNotifyService.class);

    @Autowired
    private OperationWxHandler operationWxHandler;

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private NotifyRecordService notifyRecordService;

    @Autowired
    private PaymentService paymentService;

    /**
     * 对账之后订单的费率计算结果----入账{调用方若批量请加事务处理}
     *
     * @return
     */
    public boolean orderRateSupplement(String paymentNo, ChannelRCRate channelRCRate) {
        log.info("入账-----orderRateSupplement-----paymentNo:{}", paymentNo);
        if (StringUtils.isBlank(paymentNo)) {
            log.error("paymentNo is null|订单号不能为空");
            return false;
        }
        CashierPayment cashierPayment = paymentService.queryByPamentNo(paymentNo);
        if (cashierPayment == null) {
            log.error("paymentNo not exist|订单不存在");
            return false;
        }
        // 设置渠道手续费
        if (channelRCRate.getChannelROrMBalance()) {
            cashierPayment.setChannelServiceFee(channelRCRate.getChannelServiceFee());
            cashierPayment.setCheckStatus(channelRCRate.getCheckStatus());
            cashierPayment.setCheckTime(channelRCRate.getCheckTime());
            cashierPayment.setCheckAmount(channelRCRate.getCheckAmount());
        } else {
            cashierPayment.setSettleAmount(channelRCRate.getSettleAmount());
            cashierPayment.setSettleDate(channelRCRate.getSettleDate());
            cashierPayment.setSettleStatus(channelRCRate.getSettleStatus());
        }
        // 计算商户手续费
        paymentService.updateById(cashierPayment);
        return false;
    }

    /**
     * 微信支付的回调通知
     * 首先，你需要在你的服务器上创建一个公开的 HTTP 端点，接受来自微信支付的回调通知。 当接收到回调通知，使用 notification 中的 NotificationParser 解析回调通知。
     * <p>
     * 具体步骤如下：
     * <p>
     * 使用回调通知请求的数据，构建 RequestParam。
     * HTTP 请求体 body。切记使用原始报文，不要用 JSON 对象序列化后的字符串，避免验签的 body 和原文不一致。
     * HTTP 头 Wechatpay-Signature。应答的微信支付签名。
     * HTTP 头 Wechatpay-Serial。微信支付平台证书的序列号，验签必须使用序列号对应的微信支付平台证书。
     * HTTP 头 Wechatpay-Nonce。签名中的随机数。
     * HTTP 头 Wechatpay-Timestamp。签名中的时间戳。
     * HTTP 头 Wechatpay-Signature-Type。签名类型。
     * 初始化 RSAAutoCertificateConfig。微信支付平台证书由 SDK 的自动更新平台能力提供，也可以使用本地证书。
     * 初始化 NotificationParser。
     * 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败，SDK 会抛出 ValidationException。
     * 接下来可以执行你的业务逻辑了。如果执行成功，你应返回 200 OK 的状态码。如果执行失败，你应返回 4xx 或者 5xx的状态码，例如数据库操作失败建议返回 500 Internal Server Error。
     *
     * @param request HttpServletRequest
     * @return WxNotifyVo
     */
    public WxNotifyVo dealPayResultV3(HttpServletRequest request) {
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
            // 读取请求体
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }
            String notify = sb.toString();
            log.info("微信支付通知: {}", notify);
            RequestParam requestParam = buildByHeaderRequestParam(request, notify);
            // 初始化 RSAAutoCertificateConfig。微信支付平台证书由 SDK 的自动更新平台能力提供，也可以使用本地证书。
            NotificationConfig config = operationWxHandler.getCallBackConfig(PayTypeEnum.WECHAT);
            // 验证签名并解析请求体
            NotificationParser notificationParser = new NotificationParser(config);
            // 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败，SDK 会抛出 ValidationException。
            log.info("支付通知回调:验签、解密并转换成 Transaction对象:-------");
            Transaction transaction = null;
            try {
                /**
                 * 常用的通知回调调对象类型有：
                 * 支付 Transaction
                 * 退款 RefundNotification
                 * 若 SDK 暂不支持的类型，请使用 Map.class，嵌套的 Json 对象将被转换成 LinkedTreeMap
                 */
                transaction = notificationParser.parse(requestParam, Transaction.class);
            } catch (Exception e) {
                throw new RuntimeException("支付通知回调，验签、解密失败--->" + e.getMessage());
            }
            log.info("Transaction:===>>>>支付CallBack状态" + transaction.getTradeState());
            log.info("Transaction:===>>>>" + transaction);
            // 开启线程异步通知业务侧
            taskExecutor.execute(new NotifyThreadRunnable(transaction, paymentService, notifyRecordService));
            // 接收消息成功
            return new WxNotifyVo().setCode("SUCCESS");
        } catch (Exception e) {
            log.error("解析付款通知出错：{}", e.getMessage(), e);
            return new WxNotifyVo().setCode("FAIL").setMessage(e.getMessage());
        }
    }

    /**
     * 使用回调通知请求的数据，构建 RequestParam。
     *
     * @param request HttpServletRequest
     * @param notify  notify
     * @return notify
     */
    public RequestParam buildByHeaderRequestParam(HttpServletRequest request, String notify) {
        log.info("-------------------WxPay---------GetHeader--------------------BEGIN");
        // HTTP 头 Wechatpay-Timestamp。签名中的时间戳。
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        // HTTP 头 Wechatpay-Nonce。签名中的随机数。
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        // HTTP 头 Wechatpay-Signature-Type。签名类型。
        String signType = request.getHeader("Wechatpay-Signature-Type");
        //HTTP 头 Wechatpay-Serial。微信支付平台证书的序列号，验签必须使用序列号对应的微信支付平台证书。
        String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
        //  HTTP 头 Wechatpay-Signature。应答的微信支付签名。
        String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
        log.info("-------------------WxPay---------GetHeader--------------------ENDING");
        // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serialNo)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(notify)
                .build();
        return requestParam;
    }


    /**
     * 处理退款结果
     *
     * @param request HttpServletRequest
     * @return WxNotifyVo
     */
    public WxNotifyVo dealRefundResultV3(HttpServletRequest request) {
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
            // 读取请求体
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line);
            }
            String notify = sb.toString();
            log.info("微信退款通知: {}", notify);
            RequestParam requestParam = buildByHeaderRequestParam(request, notify);
            // 初始化 RSAAutoCertificateConfig。微信支付平台证书由 SDK 的自动更新平台能力提供，也可以使用本地证书。

            NotificationConfig config = operationWxHandler.getCallBackConfig(PayTypeEnum.WECHAT);
            // 验证签名并解析请求体
            NotificationParser notificationParser = new NotificationParser(config);
            // 调用 NotificationParser.parse() 验签、解密并将 JSON 转换成具体的通知回调对象。如果验签失败，SDK 会抛出 ValidationException。
            log.info("退款通知回调:验签、解密并转换成 RefundNotification对象:-------");
            RefundNotification refundNotification = null;
            try {
                /**
                 * 常用的通知回调调对象类型有：
                 * 支付 Transaction
                 * 退款 RefundNotification
                 * 若 SDK 暂不支持的类型，请使用 Map.class，嵌套的 Json 对象将被转换成 LinkedTreeMap
                 */
                refundNotification = notificationParser.parse(requestParam, RefundNotification.class);
            } catch (Exception e) {
                throw new RuntimeException("退款通知回调，验签、解密失败(可能是微信官方的探测流量故意生成的错误签名)--->" + e.getMessage());
            }
            log.info("Transaction:===>>>>退款CallBack状态" + refundNotification.getRefundStatus());
            log.info("Transaction:===>>>>" + refundNotification);
            // 开启线程异步通知业务侧
            taskExecutor.execute(new NotifyThreadRunnable(refundNotification, paymentService, notifyRecordService));
            log.info("退款通知回调:验签、解密并转换成 RefundNotification对象:-------end");
            // 解析消息成功
            return new WxNotifyVo().setCode("SUCCESS");
        } catch (Exception e) {
            log.error("解析退款通知出错：{}", e.getMessage(), e);
            return new WxNotifyVo().setCode("FAIL").setMessage(e.getMessage());
        }
    }
}
